package org.ccay.core.expression;

import java.lang.reflect.Method;
import java.util.Map;

import org.springframework.aop.support.AopUtils;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ObjectUtils;


/**
 * 消息估值上下文
 * 
 * Created：2011-9-19
 * @author chaowangbang
 * @version:
 */
class LazyParamAwareEvaluationContext extends StandardEvaluationContext {
	
	private final ParameterNameDiscoverer paramDiscoverer;
	private final Method method;
	private final Object[] args;
	private Class<?> targetClass;
	private Map<Method, Method> methodCache;

	private boolean paramLoaded = false;
	
	/**
	 * 
	 */
	public LazyParamAwareEvaluationContext(InvocationContext invocationContext, 
			ParameterNameDiscoverer paramDiscoverer,
			Map<Method, Method> methodCache) {
		super(invocationContext.getTarget());
		
		this.paramDiscoverer = paramDiscoverer;
		this.method = invocationContext.getMethod();
		this.args = invocationContext.getParameterValues();
		this.targetClass = invocationContext.getTargetClass();
		this.methodCache = methodCache;
	}


	/* (non-Javadoc)
	 * @see org.springframework.expression.spel.support.StandardEvaluationContext#lookupVariable(java.lang.String)
	 */
	@Override
	public Object lookupVariable(String name) {
		Object variable = super.lookupVariable(name);
		if (variable != null) {
			return variable;
		}

		if (!paramLoaded) {
			paramLoaded = true;
			loadArgsAsVariables();
			variable = super.lookupVariable(name);
		}

		return variable;
	}
	
	/**
	 * 将方法调用的参数加载为变量，以便SpEL表达式进行求值。
	 * 	
	 * @author chaowangbang
	 * @since 2011-10-31
	 */
	private void loadArgsAsVariables() {
		// shortcut if no args need to be loaded
		if (ObjectUtils.isEmpty(args)) {
			return;
		}

		// save arguments as indexed variables
		for (int i = 0; i < args.length; i++) {
			super.setVariable("p" + i, args[i]);
		}

		Method targetMethod = getTargetMethod();
		String[] parameterNames = paramDiscoverer.getParameterNames(targetMethod);
		// save parameter names (if discovered)
		if (parameterNames != null) {
			for (int i = 0; i < parameterNames.length; i++) {
				super.setVariable(parameterNames[i], args[i]);
			}
		}
	}
	
	/**
	 * 获取所调用的方法
	 * 	
	 * @author chaowangbang
	 * @since 2011-10-31
	 * @return
	 */
	private Method getTargetMethod() {
		Method targetMethod = methodCache.get(method);
		if(targetMethod != null) {
			return targetMethod;
		}
		
		targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
		if(targetMethod == null) {
			targetMethod = method;
		}
		
		methodCache.put(method, targetMethod);
		
		return targetMethod;
	}

}
